﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using GridRegulator;

namespace NoahSolver
{
    class IssueHandler : IDisposable
    {
        public delegate void OnFinishCallback(Solver.Solutions sols, TileMatcher tm, Bitmap src);

        struct Transaction
        {
            public Bitmap Bmp;
            public TileMatcher TM;
            public Solver.Solutions Sols;
            public OnFinishCallback Finished;

            public Transaction(Bitmap bmp, OnFinishCallback finished)
            {
                Bmp = bmp;
                TM = new TileMatcher();
                Sols = null;
                Finished = finished;
            }
        }

        bool mDisposed = false;

        Thread mThread = new Thread(new ParameterizedThreadStart(HandlerLoop));
        AutoResetEvent mQEvent = new AutoResetEvent(false);
        Mutex mQMutex = new Mutex();
        Queue<Transaction> mTransactionQ = new Queue<Transaction>();

        public IssueHandler()
        {
            mThread.Start(this);
        }

        public virtual void Dispose(bool disposing)
        {
            if (mDisposed)
            {
                return;
            }
            if (disposing)
            {
                // not in destructor
                // clean up managed objects by calling their Dispose() methods
            }

            // clean up unmanaged objects
            if (mThread != null)
            {
                mQEvent.Set();
                mThread.Abort();
            }

            mDisposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~IssueHandler()
        {
            Dispose(false);
        }

        static void HandlerLoop(object o)
        {
            IssueHandler ih = o as IssueHandler;
            while (true)
            {
                ih.mQEvent.WaitOne();

                ih.mQMutex.WaitOne();
                while (ih.mTransactionQ.Count > 0)
                {
                    Transaction t = ih.mTransactionQ.Dequeue();

                    ih.mQMutex.ReleaseMutex();

                    bool successful = t.TM.Match(t.Bmp);
                    if (!successful)
                    {
                        t.Finished(null, null, null);
                        ih.mQMutex.WaitOne();
                        continue;
                    }

                    t.Sols = Solver.Solve(t.TM);
                    t.Finished(t.Sols, t.TM, t.Bmp);

                    ih.mQMutex.WaitOne();
                }
                ih.mQMutex.ReleaseMutex();
            }
        }

        public void AddIssue(Bitmap bmp, OnFinishCallback finished)
        {
            mQMutex.WaitOne();

            mTransactionQ.Enqueue(new Transaction(bmp, finished));
            mQEvent.Set();

            mQMutex.ReleaseMutex();
        }

        public Bitmap Reconstruct(Solver.Solutions sols, TileMatcher tm, Bitmap src)
        {
            int tilewidth = tm.TileWidth;
            int tileheight = tm.TileHeight;
            int width = tm.N * tilewidth;
            int height = tm.M * tileheight;
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.Black);

            for (int i = 0; i < tm.M; i++)
            {
                for (int j = 0; j < tm.N; j++)
                {
                    int ipet = tm[i, j];
                    if (ipet >= 0)
                    {
                        Pet pet = tm.Pets[ipet];
                        int ui = pet[0].I;
                        int uj = pet[0].J;
                        
                        int srcy = tm.IToY(ui);
                        int srcx = tm.JToX(uj);

                        g.DrawImage(src, j * tilewidth, i * tileheight,
                            new Rectangle(srcx, srcy, tilewidth, tileheight), GraphicsUnit.Pixel);
                    }
                }
            }
            
            return bmp;
        }
    }
}
